#include <fdc.h>
#include <std.h>
#include <pio.h>
#include <error.h>
#include <dint.h>
#include <biosdisk.h>
#include <memory.h>
#include <irq.h>
#include <timer.h>
#include <fdcfmt.h>
#include <fdc.h>

static struct BPB_struct	{
	u8 jmpBoot;
	u16 jmpoff;
	u8 OEMName[8];
	u16 BytsPerSec;
	u8 SecPerClus; //Sector per cluster
	u16 ResvdSecCnt;		//Reserved sector count
	u8 NumFATs;		//No. of FAT structures
	u16 RootEntCnt;
	u16 TotSec16;
	u8 Media;
	u16 FATSz16;
	u16 SecPerTrk;
	u16 NumHeads;
	u32 HiddSec;
	u32 TotSec32;
}__attribute__((packed))BPB,*BPBptr;

struct BPB_struct_FAT32	{
	u32 FATSz32;
	u16 ExtFlags;
	u16 FSVer;
	u32 RootClus;			//First cluster of the root directory
	u16 FSInfo;
	u16 BkBootSec;
	u8 Reserved[12];
	u8 DrvNum;
	u8 Reserved1;
	u8 BootSig;
	u32 VolID;
	u8 VolLab[11];
	u8 FilSysType[8];
}__attribute__((packed))BPB_fat32,*BPBptr_fat32;


struct BPB_struct_FAT12	{
	u8 DrvNum;
	u8 Reserved1;
	u8 BootSig;
	u32 VolID;
	u8 VolLab[11];
	u8 FilSysType[8];
}__attribute__((packed))BPB_fat12,*BPBptr_fat12;

static struct DIR_struct	{
	u8 Name[11];
	u8 Attr;
	u8 NTRes;		//Reserved by NT
	u8 CrtTimeTenth;  //File Creation tenth of a sec
	u16 CrtTime;		//File creation time
	u16 CrtDate;
	u16 LstAccDate;
	u16 FstClusHI;		//High of start cluster
	u16 WrtTime;
	u16 WrtDate;
	u16 FstClusLO;		//Low of start cluster
	u32 FileSize;
}__attribute__((packed))DIR,*DIR1;

#define ATTR_READ_ONLY 	0x01
#define ATTR_HIDDEN 	0x02
#define ATTR_SYSTEM 	0x04
#define ATTR_VOLUME_ID 	0x08
#define ATTR_DIRECTORY 	0x10
#define ATTR_ARCHIVE 	0x20
#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
#define ATTR_LONG_NAME_MASK ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE


//DIR.Name[0]:
#define DIR_ENTRY_FREE 0xe5
#define DIR_ENTRY_FREE_AFTER_THIS 0x0
#define DIR_KANJI 0x5



/*FAT12 code:*/
#define RootDirSectors	((BPB.RootEntCnt*32)+(BPB.BytsPerSec-1))/BPB.BytsPerSec
#define FirstRootDirSecNum (BPB.ResvdSecCnt+(BPB.NumFATs*BPB.FATSz16))


void read_fd(u8 *buf,int block,int count)
{
	u8 *fdbuf=buf;
	while(--count>=0)	{
		read_block(block,fdbuf);
		block++;
		fdbuf+=512;
	}
}

void read_cluster_fat12(u8 *clus_buf,unsigned N)
{
	unsigned FirstSectorofCluster,FirstDataSector,FATSz;
	printk(".");
	if(BPB.FATSz16!=0)
		FATSz=BPB.FATSz16;
	else
		FATSz=BPB_fat32.FATSz32;
	FirstDataSector=BPB.ResvdSecCnt+(BPB.NumFATs*FATSz)+RootDirSectors;
	FirstSectorofCluster=((N-2)*BPB.SecPerClus)+FirstDataSector;
	read_fd(clus_buf,FirstSectorofCluster,BPB.SecPerClus);
}

int determine_FAT()
{
	u32 FATSz,TotSec,DataSec,CountofClusters;
	printk("\nDetermining FD format..");
	if(BPB.FATSz16!=0)
		FATSz=BPB.FATSz16;
	else
		FATSz=BPB_fat32.FATSz32;
	if(BPB.TotSec16!=0)
		TotSec=BPB.TotSec16;
	else
		TotSec=BPB.TotSec32;
	DataSec=TotSec-(BPB.ResvdSecCnt+(BPB.NumFATs+FATSz)+RootDirSectors);
	CountofClusters=DataSec/BPB.SecPerClus;
	if(CountofClusters < 4085)	{
		return 1;
	}
	return NULL;
}


unsigned read_cluster_entry_fat12(unsigned N/*Cluster number*/)
{
	u8 *secbuff;
	unsigned FATOffset,ThisFATSecNum,ThisFATEntOffset,FAT12ClusEntryVal;
	FATOffset=N+N/2;
	ThisFATSecNum=BPB.ResvdSecCnt+(FATOffset/BPB.BytsPerSec);
	ThisFATEntOffset=FATOffset%BPB.BytsPerSec;
	/*Now read the sector number from the fd*/
	secbuff=(u8 *)get_mem(1024);	/*Read two sectors for the boundary case*/
	read_fd(secbuff,ThisFATSecNum,2);	/*Sector number is absolute since this is an fd*/
	FAT12ClusEntryVal=(u16)(*((u16 *)(secbuff+ThisFATEntOffset)));
	if(N&1)
		FAT12ClusEntryVal=FAT12ClusEntryVal >> 4;
	else
		FAT12ClusEntryVal&=0x0fff;
	return FAT12ClusEntryVal;
}

int get_name(char *name,struct DIR_struct *dir);
int valid_entry(struct DIR_struct *test);
u32 get_cluster(struct DIR_struct *dir);


int list_entries_fat12(unsigned cluster)
{
	u32 nclus=0,clus=cluster,alloc,i=0;
	u8 *clus_buffer;
	char name[20];
	u8 *root_buf;
	if(cluster==0)	{
		alloc=RootDirSectors*BPB.BytsPerSec;
		root_buf=(u8 *)get_mem(alloc);
		read_fd(root_buf,FirstRootDirSecNum,RootDirSectors);
		while(i<alloc/32)	{
			DIR1=(struct DIR_struct *)(root_buf+i*32);
			copy(&DIR,DIR1,struct DIR_struct);
			if(valid_entry(&DIR))	{
				get_name(name,&DIR);
				printk("\n%s",name);
			}
			i++;
		}
		free(root_buf);
		return NULL;
	}
	while(clus<0x0ff8)	{
		nclus++;
		clus=read_cluster_entry_fat12(clus);
	}
	alloc=BPB.BytsPerSec*BPB.SecPerClus*nclus;
	clus_buffer=(u8 *)get_mem(alloc);
	clus=cluster;
	while(clus<0x0ff8)	{
		read_cluster_fat12(clus_buffer,clus);
		clus=read_cluster_entry_fat12(clus);
	}
	while(i<(alloc/32))	{
		DIR1=(struct DIR_struct *)(clus_buffer+i*32);
		copy(&DIR,DIR1,struct DIR_struct);
		get_name(name,&DIR);
		if(valid_entry(&DIR))	{
			printk("\n%s",name);
		}
		i++;
	}
	free(clus_buffer);
	return SUCCESS;
}

int get_entry_fat12(u32 cluster,char *filename)
{
	u32 nclus=0,clus=cluster,alloc,i=0;
	u8 *clus_buffer;
	char name[20];
	u8 *root_buf;
	printk("\nFinding entry:%s in %d",filename,cluster);
	if(cluster==0)	{
		alloc=RootDirSectors*BPB.BytsPerSec;
		root_buf=(u8 *)get_mem(alloc);
		read_fd(root_buf,FirstRootDirSecNum,RootDirSectors);
		while(i<alloc/32)	{
			DIR1=(struct DIR_struct *)(root_buf+i*32);
			copy(&DIR,DIR1,struct DIR_struct);
			if(valid_entry(&DIR))	{
				get_name(name,&DIR);
				printk("\n%s",name);
				if(strcmp(name,filename)==NULL)	{
					printk("\n--found");
					free(root_buf);
					return NULL;
				}
			}
			i++;
		}
		free(root_buf);
		error("File not found in root");
		return -1;
	}
	while(clus<0x0ff8)	{
		nclus++;
		clus=read_cluster_entry_fat12(clus);
	}
	alloc=BPB.BytsPerSec*BPB.SecPerClus*nclus;
	clus_buffer=(u8 *)get_mem(alloc);
	clus=cluster;
	while(clus<0x0ff8)	{
		read_cluster_fat12(clus_buffer,clus);
		clus=read_cluster_entry_fat12(clus);
	}
	while(i<(alloc/32))	{
		DIR1=(struct DIR_struct *)(clus_buffer+i*32);
		copy(&DIR,DIR1,struct DIR_struct);
		if(valid_entry(&DIR))	{
			get_name(name,&DIR);
			if(strcmp(name,filename)==NULL)	{
				free(clus_buffer);
				printk("\n--found");
				return SUCCESS;
			}
		}
		i++;
	}
	error("File not found");
	free(clus_buffer);
	return FAILED;		
}

int read_file_fat12(u8 *ret,struct DIR_struct *dir)
{
	u8 *file_buffer=ret;
	u32 clus,n_clus;
	n_clus=dir->FileSize/(BPB.SecPerClus*BPB.BytsPerSec)+1;
	printk("\nReading file...");
	clus=get_cluster(dir);
	printk("\nStart cluster:%d",clus);
	while(clus<0x0ff8 && --n_clus>=0)	{
		read_cluster_fat12(file_buffer,clus);
		file_buffer+=(BPB.BytsPerSec)*(BPB.SecPerClus);
		clus=read_cluster_entry_fat12(clus);
	}
	return NULL;
}

static unsigned current_cluster_fat12=0;
static char current_directory_fat12[20];
u8 *go_entry_fat12(char *name)
{
	u8 *buf=NULL;
	if(get_entry_fat12(current_cluster_fat12,name)==NULL)	{
		if(DIR.Attr & ATTR_DIRECTORY)	{
			current_cluster_fat12=get_cluster(&DIR);
			get_name(current_directory_fat12,&DIR);
			list_entries_fat12(current_cluster_fat12);
		} else	{
			buf=(u8 *)get_mem(DIR.FileSize);
			read_file_fat12(buf,&DIR);
		}
	} else	{
		error("\nNo such entry");
	}
	return buf;
}

int query_entry_fat12(char *name)
{
	if(get_entry_fat12(current_cluster_fat12,name)==NULL)	{
		return SUCCESS;
	} else	{
		error("\nNo such entry");
		return FAILED;
	}
}

int query_open_fd(char *name)
{
	int i=1;
	char *opt;
	printk("\nQuerying fd:%s",name);
	current_cluster_fat12=0;
	opt=(char *)get_mem(256);
	if(*name!='/')
		return FAILED;
	getstringparameter(name,opt,'/',i);
	if(strlen(opt)==NULL)	{
		list_entries_fat12(current_cluster_fat12);
		return SUCCESS;
	}
	while(strlen(opt)!=NULL)	{
		if(query_entry_fat12(opt)==FAILED)
			return FAILED;
		getstringparameter(name,opt,'/',++i);
	}
	return SUCCESS;
}


//File string parser and eye opener
u8 *open_fd(char *name)
{
	int i=1;
	char *opt;
	u8 *buf=NULL;
	printk("\nOpening:%s",name);
	current_cluster_fat12=0;
	opt=(char *)get_mem(256);
	if(*name!='/')
		return NULL;
	getstringparameter(name,opt,'/',i);
	if(strlen(opt)==NULL)	{
		list_entries_fat12(current_cluster_fat12);
		return NULL;
	}
	while(strlen(opt)!=NULL)	{
		buf=go_entry_fat12(opt);
		getstringparameter(name,opt,'/',++i);
	}
	return buf;
}

int read_bpb_fd()
{
	u8 *buf;
	printk("\nReading BPB of floppy..");
	buf=(u8 *)get_mem(512);
	read_fd(buf,0,1);
	BPBptr=(struct BPB_struct *)buf;
	BPBptr_fat32=(struct BPB_struct_FAT32 *)(buf+36);
	BPBptr_fat12=(struct BPB_struct_FAT12 *)(buf+36);
	copy(&BPB,BPBptr,struct BPB_struct);
	copy(&BPB_fat32,BPBptr_fat32,struct BPB_struct_FAT32);
	copy(&BPB_fat12,BPBptr_fat12,struct BPB_struct_FAT12);
	free(buf);
	if(determine_FAT())	{
		printk("\nFound FAT12 floppy..");
		return SUCCESS;
	}
	return FAILED;
}


int init_fdcfmt()
{
	return SUCCESS;
}

